package org.me.marble;

import javax.microedition.khronos.opengles.GL10;

/**
*
* @author tungi
*/

public class Shadow{
    private float[] shadowMatrix=new float[16];
    private GameRenderer _renderer;

    public Shadow(GameRenderer renderer){
        _renderer=renderer;
    }
    
    public void GenerateShadowMatrix(Vector normal,Vector point,Vector light){
		float d=normal.getX()*light.getX()+normal.getY()*light.getY()+normal.getZ()*light.getZ();
		float c=normal.getX()*point.getX()+normal.getY()*point.getY()+normal.getZ()*point.getZ()-d;
	
		shadowMatrix[0]=light.getX()*normal.getX()+c;
		shadowMatrix[4]=light.getX()*normal.getY();
		shadowMatrix[8]=light.getX()*normal.getZ();
		shadowMatrix[12]=-light.getX()*c-light.getX()*d;
	
		shadowMatrix[1]=light.getY()*normal.getX();
		shadowMatrix[5]=light.getY()*normal.getY()+c;
		shadowMatrix[9]=light.getY()*normal.getZ();
		shadowMatrix[13]=-light.getY()*c-light.getY()*d;
	
		shadowMatrix[2]=light.getZ()*normal.getX();
		shadowMatrix[6]=light.getZ()*normal.getY();
		shadowMatrix[10]=light.getZ()*normal.getZ()+c;
		shadowMatrix[14]=-light.getZ()*c-light.getZ()*d;
	
		shadowMatrix[3]=normal.getX();
		shadowMatrix[7]=normal.getY();
		shadowMatrix[11]=normal.getZ();
		shadowMatrix[15]=-d;
    }

    public void drawShadow(GL10 gl){         
        String quality=new String(OptionsLibrary.graphics_quality);
        AvailableObject ao;
        //clear
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | 
        		GL10.GL_DEPTH_BUFFER_BIT | GL10.GL_STENCIL_BUFFER_BIT);
        gl.glLoadIdentity();
        //set camera position
        _renderer.cam.setLookPosition(gl,_renderer.marble_move);        
        //move everything down
        gl.glTranslatef(0.0f,-1.0f,0.0f);
        
        //gl.glClearStencil(0);
        //get the radius of the marble
        float rad=_renderer.marble.getRadius();
        //Turn off writing to the Color Buffer and Depth Buffer
        //We want to draw to the Stencil Buffer only
        gl.glColorMask(false,false,false,false);
        gl.glDepthMask(false);
        //Enable the Stencil Buffer
        gl.glEnable(GL10.GL_STENCIL_TEST);
        //Set 1 into the stencil buffer
        gl.glStencilFunc(GL10.GL_ALWAYS,1,0xFFFFFFFF);
        gl.glStencilOp(GL10.GL_REPLACE,GL10.GL_REPLACE,GL10.GL_REPLACE);

        //Draw the path
        gl.glPushMatrix();
            gl.glTranslatef(0.0f,-rad,0.0f);
            _renderer.path.draw(gl);
        gl.glPopMatrix();

        //######################################################################
        //Turn on Color Buffer and Depth Buffer
        gl.glColorMask(true,true,true,true);
        gl.glDepthMask(true);
        //Only write to the Stencil Buffer where 1 is set
        gl.glStencilFunc(GL10.GL_EQUAL,1,0xFFFFFFFF);
        //Keep the content of the Stencil Buffer
        gl.glStencilOp(GL10.GL_KEEP,GL10.GL_KEEP,GL10.GL_KEEP);

        //Draw the path again
        gl.glPushMatrix();
            gl.glTranslatef(0.0f,-rad,0.0f);
            _renderer.path.draw(gl);
        gl.glPopMatrix();

        //draw objects first####################################################
        gl.glPushMatrix();
            //Set our color
            gl.glColor4f(0.0f,0.0f,0.0f,0.3f);
            _renderer.light.disableLight(gl);
            gl.glDisable(GL10.GL_DEPTH_TEST);

            //in this case textures will be not drawn
            OptionsLibrary.graphics_quality="low";

            gl.glEnable(GL10.GL_BLEND);
            gl.glBlendFunc(GL10.GL_SRC_ALPHA,GL10.GL_ONE_MINUS_SRC_ALPHA);            

            //Keep the content of the Stencil Buffer and increase
            //when z passed (not Carmacks fail test)
            gl.glStencilOp(GL10.GL_KEEP,GL10.GL_KEEP,GL10.GL_INCR);

            //draw available objects first time#################################
            for(int i=0;i<_renderer.objects.size();++i){
                ao=_renderer.objects.get(i);
                gl.glPushMatrix();
                    //Calculate the projected shadow
                    GenerateShadowMatrix(
                            new Vector(ao.getUp().getX(),
                                       ao.getUp().getY()*-1,
                                       ao.getUp().getZ()),
                            new Vector(ao.getCentre().getX(),
                                       ao.getCentre().getY()-rad,
                                       ao.getCentre().getZ()),
                            _renderer.light.getPosition());
                    gl.glMultMatrixf(shadowMatrix,0);

                    //Draw the object
                    if(!_renderer.isPause())
                        ao.update();
                    ao.draw(gl);
                    //and Pop the matrix from the stack
                gl.glPopMatrix();
            }

            //draw marble first time############################################   
            //Push the current matrix to the stack
            gl.glPushMatrix();
                //Calculate the projected shadow
                GenerateShadowMatrix(
                        new Vector(_renderer.marble_move.getUp().getX(),
                                   _renderer.marble_move.getUp().getY()*-1,
                                   _renderer.marble_move.getUp().getZ()),
                        new Vector(_renderer.marble_move.getPos().getX(),
                                   _renderer.marble_move.getPos().getY()-rad,
                                   _renderer.marble_move.getPos().getZ()),
                        _renderer.light.getPosition());
                gl.glMultMatrixf(shadowMatrix,0);

                //Draw the marble
                if(!_renderer.isPause())
                    _renderer.marble_move.move(gl,_renderer,_renderer.isPause());
                _renderer.marble_move.transfom(gl);
                _renderer.marble.draw(gl);
            //and Pop the matrix from the stack
            gl.glPopMatrix();
            //##################################################################
            //restore original settings            
            gl.glDisable(GL10.GL_BLEND);
            OptionsLibrary.graphics_quality=quality;
            gl.glEnable(GL10.GL_DEPTH_TEST);
            _renderer.light.enableLight(gl);
        gl.glPopMatrix();

        //Disable Stencil Buffer
        gl.glDisable(GL10.GL_STENCIL_TEST);

        //draw objects again####################################################
        for(int i=0;i<_renderer.objects.size();++i){
            gl.glPushMatrix();
                ao=_renderer.objects.get(i);
                ao.draw(gl);
            gl.glPopMatrix();
        }
        
        gl.glPushMatrix();
            //Draw the marble again
            _renderer.marble_move.transfom(gl);
            _renderer.marble.draw(gl);
        gl.glPopMatrix();
    }
}